package com.itheima.j2cache.aop.processor;

import com.itheima.j2cache.anno.Cache;
import com.itheima.j2cache.model.AnnotationInfo;
import com.itheima.j2cache.model.CacheHolder;
import lombok.extern.slf4j.Slf4j;
import net.oschina.j2cache.CacheObject;
import org.aspectj.lang.ProceedingJoinPoint;

import java.lang.annotation.Annotation;

/**
 * 缓存注解处理器
 *
 * @author llhuang10
 * @date 2021/10/8 9:25
 */
@Slf4j
public class CacheAnnotationProcessor extends AbstractCacheAnnotationProcessor {


    private AnnotationInfo<Cache> annotationInfo;

    /**
     * 初始化缓存注解处理器, 同时将相关的对象进行初始化
     *
     * @param joinPoint  切点
     * @param annotation 注解
     */
    public CacheAnnotationProcessor(ProceedingJoinPoint joinPoint, Annotation annotation) {
        // 先来显式调用父类的构造器, 这样的话, 父类中的cacheChannel成员变量才能在父类构造器中初始化
        super();
        // 创建注解信息对象
        annotationInfo = super.getAnnotationInfo(joinPoint, (Cache) annotation);
    }


    /**
     * 具体缓存的处理逻辑
     *
     * @param joinPoint 切点
     * @return
     */
    @Override
    public Object process(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = null;
        boolean existsCache = false;
        // 1.获取缓存数据 -- 不一定获取到
        CacheHolder cacheHolder = this.getCache(annotationInfo);
        if (cacheHolder.isExistsCache()) {
            // 2.如果缓存数据存在则直接返回 (相当于controller的目标方法没有执行) -->
            // 不调用joinPoint.process() --> 就不会执行那个目标controller方法, 直接将返回值返回给前端
            // 获取缓存结果数据
            result = cacheHolder.getValue();
            existsCache = true;
        }

        // 3.如果缓存数据不存在, 放行调用Controller的目标方法
        if (!existsCache) {
            result = this.invoke(joinPoint);
            // 4.将目标方法的返回值载入缓存
            this.setCache(result);
        }

        // 5.将结果返回
        return result;
    }


    /**
     * 获取缓存数据
     *
     * @param annoInfo Cache相关信息包装
     * @return 缓存结果封装
     */
    private CacheHolder getCache(AnnotationInfo<Cache> annoInfo) {
        Object value = null;

        String region = annoInfo.getRegion();
        String key = annoInfo.getKey();
        boolean exists = cacheChannel.exists(region, key);
        if (exists) {
            CacheObject cacheObject = cacheChannel.get(region, key);
            // 获取缓存结果数据
            value = cacheObject.getValue();
            return CacheHolder.newResult(value, true);
        }

        return CacheHolder.newResult(value, false);
    }

    /**
     * 调用目标方法
     *
     * @param joinPoint 切点
     * @return 调用目标方法返回结果
     */
    public Object invoke(ProceedingJoinPoint joinPoint) throws Throwable {
        return joinPoint.proceed(joinPoint.getArgs());
    }

    /**
     * 设置缓存数据
     *
     * @param result 缓存数据
     */
    public void setCache(Object result) {
        cacheChannel.set(annotationInfo.getRegion(), annotationInfo.getKey(), result);
    }
}
